package org.jpropelleralt.reaction;

import java.util.IdentityHashMap;
import java.util.Map;

import org.jpropelleralt.box.Box;
import org.jpropelleralt.change.Change;
import org.jpropelleralt.universe.Universe;

/**
 * A {@link Reaction} reacts to changes in any of
 * a set of input {@link Box}es. 
 * 
 * When reacting, a {@link Reaction} may do anything
 * it wants outside the {@link Universe}, but inside
 * the {@link Universe} it may react by declaring it
 * will change one or more output {@link Box}es (which may
 * be the same or different from input {@link Box}es).
 * It may also declare it intends to change an 
 * unknown set of output {@link Box}es, where it cannot predict
 * in advance which ones.
 * 
 * Later (but before any reading of any of the 
 * output {@link Box}es) the {@link Reaction}
 * will be provided with a complete map of changes
 * that occurred during the change propagation that called
 * {@link #react(Map)}. The next step is that the {@link Reaction} will be 
 * asked to actually apply the changes it declared.
 * When doing this, it may read any of its input {@link Box}es,
 * and write to any of the declared output {@link Box}es, but
 * must not read or write any other {@link Box}es. It may also
 * use the data supplied in any {@link Change}s. It should be careful
 * to then forget any {@link Change}s that it has cached.
 * 
 * A {@link Reaction}'s input {@link Box}es must be
 * immutable (the set of {@link Box}es does NOT change
 * after construction).
 * 
 * A {@link Reaction}'s output {@link Box}es can be different
 * each time a {@link Reaction} is notified of a change.
 *
 * Implementations of {@link Reaction} will be most efficient when
 * they declare known output {@link Box}es, since they can then
 * be applied lazily. When output {@link Box}es are unknown,
 * the {@link Reaction} will be applied before ANY {@link Box}
 * in the {@link Universe} is read, and this is less efficient.
 * 
 * A {@link Reaction} may hold state, but only to allow it to apply
 * itself more efficiently - it MUST only set output {@link Box}es
 * based on the CURRENT value of input {@link Box}es, and the
 * {@link Change}s it is informed of via {@link #apply(boolean)} or
 * preferably {@link #completeChanges(Map)}.
 * Such state should be carefully handled to prevent any side effects, use
 * of stale data, etc. Nearly all {@link Reaction}s should only have
 * immutable state, but some complex provided implementations (e.g.
 * for paths) have internal state used to filter changes and reduce
 * the number of times the {@link Reaction} is applied.
 */
public interface Reaction {

	/**
	 * React to {@link Change}s by declaring what {@link Change}s the {@link Reaction} will make when applied.
	 * @param changes	{@link Map} from each changed {@link Box} to the {@link Change} it has had at this
	 * 					stage of {@link Change} propagation. Bear in mind that this is probably a partial change
	 * 					set, since calls to {@link #react(Map)} are part of the propagation. When the propagation
	 * 					is complete, {@link #completeChanges(Map)} will be called with a complete set of changes
	 * 					for the propagation. This can be used by {@link Reaction}s that want to use only complete
	 * 					changes, perhaps to work out exactly what they will do when (if) {@link #apply(boolean)} is called.
	 * @return 			{@link Map} from each {@link Box} the {@link Reaction} will change when applied,
	 * 					to the {@link Change} it will make.  MUST be an {@link IdentityHashMap}, or other 
	 * 					{@link Map} using identity for equality.
	 * 					If null is present as a key, the {@link Reaction} may change any {@link Box} 
	 * 					when applied (see class docs also).
	 */
	public Map<Box, Change> react(Map<Box, Change> changes);
	
	/**
	 * Called at the end of a complete propagation of changes resulting from an initial change, allowing
	 * {@link Reaction}s to see exactly what has happened, and optionally ask to be applied as soon as
	 * possible, rather than waiting to be applied lazily. 
	 * If required, changes can be retained until {@link #apply(boolean)} is called. Note that this method will 
	 * always be called after a change propagation that has any changes on {@link Box}es this {@link Reaction} has
	 * been added to (i.e. after any change propagation where {@link #react(Map)} was called), and BEFORE
	 * {@link #apply(boolean)} is called. This means that {@link Reaction}s are guaranteed that if they
	 * are applied, they have already been shown the complete changes. This is useful for some (relatively
	 * rare and complex) {@link Reaction}s that need to know the complete set of {@link Change}s that have
	 * occurred in order to apply properly.
	 * Many {@link Reaction}s will just ignore this method, returning false.
	 * @param changes	The complete set of changes for the propagation.
	 * @return			True if the {@link Reaction} wishes to be applied as soon as possible.
	 */
	public boolean completeChanges(Map<Box, Change> changes);
	
	/**
	 * Apply the {@link Reaction}
	 * @param propagate		Whether changes should be propagated - generally true
	 * 						if applying to unknown targets, or false if to known 
	 * 						targets. Must be passed to the appropriate method
	 * 						of changed {@link Box}es.
	 */
	public void apply(boolean propagate);
	
}
